/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "napi/pag_napi_pagPlayer.h"
#include "napi/pag_napi_pagFile.h"
#include "manager/plugin_manager.h"
#include "napi/pag_napi_comPosition.h"
#include "napi/pag_napi_matrix.h"
#include "hilog/log.h"

EXTERN_C_START
namespace NativeXComponentSample {
const unsigned int LOG_PRINT_DOMAIN = 0XFF00;

void PAGPlayerInit(napi_env env, napi_value exports)
{
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
    if ((env == nullptr) || (exports == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
        return;
    }
    // PAGPlayer类js和native的映射
    napi_property_descriptor classPlayerProp[] = {
        {"setComposition", nullptr, PAGPlayer::SetComposition, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"duration", nullptr, PAGPlayer::Duration, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"currentFrame", nullptr, PAGPlayer::CurrentFrame, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getProgress", nullptr, PAGPlayer::GetProgress, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"play", nullptr, PAGPlayer::Prepare, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"pagInit", nullptr, PAGPlayer::PagInit, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"pause", nullptr, PAGPlayer::Pause, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"resume", nullptr, PAGPlayer::Resume, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"stop", nullptr, PAGPlayer::Stop, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getState", nullptr, PAGPlayer::GetState, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"addPlayStateListener", nullptr, PAGPlayer::AddPlayerListener, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"setRepeatCount", nullptr, PAGPlayer::SetRepeatCount, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setProgress", nullptr, PAGPlayer::SetProgress, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setSurface", nullptr, PAGPlayer::SetSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setScaleMode", nullptr, PAGPlayer::SetScaleMode, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"scaleMode", nullptr, PAGPlayer::ScaleMode, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"nativeSetMatrix", nullptr, PAGPlayer::NativeSetMatrix, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setCacheEnabled", nullptr, PAGPlayer::SetCacheEnabled, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setCacheScale", nullptr, PAGPlayer::SetCacheScale, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setMaxFrameRate", nullptr, PAGPlayer::SetMaxFrameRate, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setUseDiskCache", nullptr, PAGPlayer::SetUseDiskCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setVideoEnabled", nullptr, PAGPlayer::SetVideoEnabled, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"useDiskCache", nullptr, PAGPlayer::UseDiskCache, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"videoEnabled", nullptr, PAGPlayer::VideoEnabled, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"maxFrameRate", nullptr, PAGPlayer::MaxFrameRate, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"cacheEnabled", nullptr, PAGPlayer::CacheEnabled, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"cacheScale", nullptr, PAGPlayer::CacheScale, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getComposition", nullptr, PAGPlayer::GetComposition, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getLayersUnderPoint", nullptr, PAGPlayer::GetLayersUnderPoint, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"flush", nullptr, PAGPlayer::FlushAndFenceSync, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"addRepeatCountListener", nullptr, PAGPlayer::RegisterRepeatCountCallback, nullptr, nullptr, nullptr,
         napi_default, nullptr},
        {"addCurrentFrameListener", nullptr, PAGPlayer::RegisterCurrentFrameCallback, nullptr, nullptr, nullptr,
         napi_default, nullptr},
        {"removePlayStateListener", nullptr, PAGPlayer::RemovePlayStateListener, nullptr, nullptr, nullptr,
         napi_default, nullptr},
        {"removeRepeatCountListener", nullptr, PAGPlayer::RemoveRepeatCountListener, nullptr, nullptr, nullptr,
         napi_default, nullptr},
        {"removeCurrentFrameListener", nullptr, PAGPlayer::RemoveCurrentFrameListener, nullptr, nullptr, nullptr,
         napi_default, nullptr},
        {"freeCache", nullptr, PAGPlayer::FreeCache, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_value pagPlayer = nullptr;
    const char *classPlayerBindName = "PAGPlayer";
    int methodPlayerSize = std::end(classPlayerProp) - std::begin(classPlayerProp);
    if (napi_define_class(env, classPlayerBindName, strlen(classPlayerBindName), PAGPlayer::JsPlayerConstructor,
        nullptr, methodPlayerSize, classPlayerProp, &pagPlayer) != napi_ok) {
        return;
    }
    if (napi_create_reference(env, pagPlayer, 1, &PAGPlayer::sJSPlayerConstructor_) != napi_ok) {
        return;
    }
    if (napi_set_named_property(env, exports, "PAGPlayer", pagPlayer) != napi_ok) {
        return;
    }
    napi_define_properties(env, exports, sizeof(classPlayerProp) / sizeof(classPlayerProp[0]), classPlayerProp);
}

void PAGFileInit(napi_env env, napi_value exports)
{
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
    if ((env == nullptr) || (exports == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
        return;
    }

    // PAGFile类js和native的映射
    napi_property_descriptor classFileProp[] = {
        {"loadFromPath", nullptr, PAGFile::LoadFromPath, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setDuration", nullptr, PAGFile::SetDuration, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"duration", nullptr, PAGFile::Duration, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setStartTime", nullptr, PAGFile::SetStartTime, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"numImages", nullptr, PAGFile::NumImage, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"numTexts", nullptr, PAGFile::NumTexts, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"numVideos", nullptr, PAGFile::NumVideos, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"path", nullptr, PAGFile::Path, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"maxSupportedTagLevel", nullptr, PAGFile::MaxSupportedTagLevel, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"getEditableIndices", nullptr, PAGFile::GetEditableIndices, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"timeStretchMode", nullptr, PAGFile::TimeStretchMode, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"tagLevel", nullptr, PAGFile::TagLevel, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setTimeStretchMode", nullptr, PAGFile::SetTimeStretchMode, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_value pagFile = nullptr;
    const char *classFileBindName = "PAGFile";
    int methodFileSize = std::end(classFileProp) - std::begin(classFileProp);
    if (napi_define_class(env, classFileBindName, strlen(classFileBindName), PAGFile::JsFileConstructor, nullptr,
        methodFileSize, classFileProp, &pagFile) != napi_ok) {
        return;
    }
    if (napi_create_reference(env, pagFile, 1, &PAGFile::sJSFileConstructor_) != napi_ok) {
        return;
    }
    if (napi_set_named_property(env, exports, "PAGFile", pagFile) != napi_ok) {
        return;
    }
    napi_define_properties(env, exports, sizeof(classFileProp) / sizeof(classFileProp[0]), classFileProp);
}

void PAGCompositionInit(napi_env env, napi_value exports)
{
    // PAGComposition类js和native的映射
    napi_property_descriptor classCompositionProp[] = {
        {"init", nullptr, PAGComposition::PAGCompositionInit, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"addLayer", nullptr, PAGComposition::AddLayer, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getLayerAt", nullptr, PAGComposition::GetLayerAt, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"removeLayer", nullptr, PAGComposition::RemoveLayer, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"removeAllLayers", nullptr, PAGComposition::RemoveAllLayers, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"numChildren", nullptr, PAGComposition::NumChildren, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"addLayerAt", nullptr, PAGComposition::AddLayerAt, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"removeLayerAt", nullptr, PAGComposition::RemoveLayerAt, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_value pagCompositionInit = nullptr;
    const char *classCompositionBindName = "PAGComposition";
    int methodCompositionSize = std::end(classCompositionProp) - std::begin(classCompositionProp);
    if (napi_define_class(env, classCompositionBindName, strlen(classCompositionBindName),
                          PAGComposition::JsPlayerConstructor, nullptr, methodCompositionSize, classCompositionProp,
                          &pagCompositionInit) != napi_ok) {
        return;
    }
    if (napi_create_reference(env, pagCompositionInit, 1, &PAGComposition::sJSPlayerConstructor_) != napi_ok) {
        return;
    }
    if (napi_set_named_property(env, exports, "PAGComposition", pagCompositionInit) != napi_ok) {
        return;
    }
    napi_define_properties(env, exports, sizeof(classCompositionProp) / sizeof(classCompositionProp[0]),
                           classCompositionProp);
}

void PAGMatrixInit(napi_env env, napi_value exports)
{
    // PAGMatrix类js和native的映射
    napi_property_descriptor classMatrixProp[] = {
        {"setValues", nullptr, PAGMatrix::Setvalues, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getScaleX", nullptr, PAGMatrix::GetScaleX, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getScaleY", nullptr, PAGMatrix::GetScaleY, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getSkewX", nullptr, PAGMatrix::GetSkewX, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getSkewY", nullptr, PAGMatrix::GetSkewY, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getTranslateX", nullptr, PAGMatrix::GetTranslateX, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getTranslateY", nullptr, PAGMatrix::GetTranslateY, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_value pagMatrix = nullptr;
    const char *classMatrixBindName = "PAGMatrix";
    int methodMatrixSize = std::end(classMatrixProp) - std::begin(classMatrixProp);
    if (napi_define_class(env, classMatrixBindName, strlen(classMatrixBindName), PAGMatrix::JsMatrixConstructor,
                          nullptr, methodMatrixSize, classMatrixProp, &pagMatrix) != napi_ok) {
        return;
    }
    if (napi_create_reference(env, pagMatrix, 1, &PAGMatrix::sJSMatrixConstructor_) != napi_ok) {
        return;
    }
    if (napi_set_named_property(env, exports, "PAGMatrix", pagMatrix) != napi_ok) {
        return;
    }
    napi_define_properties(env, exports, sizeof(classMatrixProp) / sizeof(classMatrixProp[0]), classMatrixProp);
}

static napi_value Init(napi_env env, napi_value exports)
{
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
    if ((env == nullptr) || (exports == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
        return nullptr;
    }
    PAGPlayerInit(env, exports);
    PAGFileInit(env, exports);
    PAGCompositionInit(env, exports);
    PAGMatrixInit(env, exports);
    // XComponent的映射
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
    if ((env == nullptr) || (exports == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
        return nullptr;
    }

    napi_property_descriptor desc[] = {
        {"getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr}};
    if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
        return nullptr;
    }

    PluginManager::GetInstance()->Export(env, exports);
    return exports;
}
EXTERN_C_END
static napi_module pagModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = NativeXComponentSample::Init,
    .nm_modname = "native_libpag",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&pagModule); }
} // namespace NativeXComponentSample